package service

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"inventory/api/bmember"
	"inventory/api/inventory"
	"inventory/internal/constant"
	"inventory/internal/model"
	"inventory/internal/pkg/generator"
	"inventory/pkg/orm"
	"inventory/pkg/util"
	"strconv"
	"time"
)

// CreateRefundOrder 创建退款单
func (s *Service) CreateRefundOrder(ctx context.Context, req *inventory.CreateRefundOrderRequest) (reply *inventory.CreateOrderReply, err error) {
	err = s.dao.Transaction(ctx, func(ctx context.Context) (err error) {
		now := time.Now()
		var (
			orderNumber string
			warehouse   *model.Warehouse
			platform    *model.ExpressPlatform
			orderRemark []byte
			prodCount   = 0
			existOrder  *model.ProcessOrder
		)
		if existOrder, err = s.dao.GetProcessOrderByExtOrderNumberAndSource(
			ctx, req.ExtOrderNumber, int(inventory.SourceEnum_MALL_ORDER)); err != nil {
			if errors.Cause(err) == gorm.ErrRecordNotFound {
				err = nil
			} else {
				return
			}
		} else {
			// 幂等返回成功
			reply = &inventory.CreateOrderReply{
				OrderNumber:    existOrder.OrderNumber,
				ExtOrderNumber: req.ExtOrderNumber,
				Status:         uint32(existOrder.Status),
			}
			return
		}

		if warehouse, err = s.dao.GetWarehouseInfoInternal(ctx, req.Id); err != nil {
			err = constant.ErrWarehouseNotExist
			return
		}
		if platform, err = s.dao.GetExpressPlatform(ctx, req.PlatformId); err != nil {
			err = constant.ErrExpressPlatformNotExist
			return
		}
		if req.Remark != nil {
			if orderRemark, err = json.Marshal(req.Remark); err != nil {
				return
			}
		}
		// 查询bom表构造子明细
		storageKeys := make([]string, len(req.OperationDetails))
		for i, d := range req.OperationDetails {
			if util.InStringArray(d.StorageKey, storageKeys) {
				// 冻结单明细内不允许出现重复的商品关键字
				err = constant.ErrDuplicateStorageKeys
				return
			}
			storageKeys[i] = d.StorageKey
			prodCount -= int(d.Quantity)
		}
		var bomArray []*model.BomConfig
		bomMap := make(map[string]map[string]float64)
		if bomArray, err = s.dao.FindBomByStorageKeys(ctx, storageKeys); err != nil {
			return
		}
		materialIds := make([]uint64, len(bomArray))
		materialRemarkMap := make(map[string]json.RawMessage)
		for _, b := range bomArray {
			key := "m-" + strconv.Itoa(int(b.MaterialID))
			if _, ok := bomMap[b.StorageKey]; !ok {
				bomMap[b.StorageKey] = make(map[string]float64)
			}
			bomMap[b.StorageKey][key] = b.Quantity
			materialIds = append(materialIds, b.MaterialID)
		}
		if len(materialIds) != 0 {
			if materialRemarkMap, err = s.dao.BuildProcessMaterialRemarks(ctx, materialIds); err != nil {
				return
			}
		}
		if orderNumber, err = generator.GenerateOrderNumber(now, constant.OrderTypeRefund, req.MemberId); err != nil {
			return
		}
		processOrder := model.ProcessOrder{
			ID:               0,
			OrderNumber:      orderNumber,
			ExtOrderNumber:   req.ExtOrderNumber,
			FreezeOrderID:    sql.NullInt64{},
			Direction:        constant.DirectionOut,
			Source:           uint8(req.Source),
			WarehouseID:      warehouse.ID,
			ProdTrans:        -int64(req.ProdTrans),
			ExpectDeliverFee: 0,
			ActualDeliverFee: 0,
			AddressInfo:      nil,
			MemberID:         req.MemberId,
			DeliverType:      uint8(inventory.DeliverType_ORDINARY),
			SubmitTime:       now,
			Status:           uint8(inventory.ProcessOrderStatus_ProcessOrderReturning),
			RefundFlag:       0,
			Remark:           orderRemark,
			ProdCount:        uint32(prodCount),
			NotifyFlag:       false,
			UpdateTime:       now,
			Operator:         0,
		}
		if err = s.dao.Create(ctx, &processOrder); err != nil {
			return
		}
		for _, d := range req.OperationDetails {
			var detailRemark []byte
			// 商品编码 = 仓储对象key
			if detailRemark, err = json.Marshal(&model.OperationDetailRemark{
				Remark: model.Remark{
					Code:    d.StorageKey,
					Name:    d.Remark.Name,
					Unit:    d.Remark.Unit,
					SkuName: d.Remark.SkuName,
				},
				Reason: d.Remark.Reason,
			}); err != nil {
				return
			}
			detail := model.ProcessDetail{
				ID:          0,
				OrderID:     processOrder.ID,
				StorageType: uint8(inventory.StorageType_PROD),
				StorageKey:  d.StorageKey,
				ParentID:    0,
				WarehouseID: warehouse.ID,
				Price:       d.Price,
				Quantity:    -float64(d.Quantity),
				Status:      constant.ProcessDetailShipped,
				NotifyFlag:  false,
				Remark:      detailRemark,
				CreateTime:  now,
				UpdateTime:  now,
				Origin:      -int64(d.Origin),
				Discount:    -int64(d.Discount),
				Trans:       -int64(d.Trans),
			}
			if bb, ok := bomMap[d.StorageKey]; ok {
				if err = s.dao.Create(ctx, &detail); err != nil {
					return
				}
				for storageKey, quantity := range bb {
					if err = s.dao.Create(ctx, &model.ProcessDetail{
						OrderID:      processOrder.ID,
						StorageType:  uint8(inventory.StorageType_MATERIAL),
						StorageKey:   storageKey,
						ParentID:     detail.ID,
						WarehouseID:  warehouse.ID,
						Price:        0,
						Quantity:     quantity * -float64(d.Quantity),
						Status:       constant.ProcessDetailShipped,
						NotifyFlag:   false,
						Remark:       orm.JSON(materialRemarkMap[storageKey]),
						CreateTime:   now,
						UpdateTime:   now,
						Origin:       0,
						Discount:     0,
						Trans:        0,
						IsAccounting: true,
					}); err != nil {
						return
					}
				}
			} else {
				detail.IsAccounting = true
				if err = s.dao.Create(ctx, &detail); err != nil {
					return
				}
			}
		}
		expressOrder := model.ExpressOrder{
			OrderType:   1,
			OrderNumber: processOrder.OrderNumber,
			OutTradeNo:  sql.NullString{},
			TradeNo:     req.PlatformOrderNumber,
			PlatformID:  platform.ID,
			WarehouseID: warehouse.ID,
			Lat:         warehouse.Lat,
			Lng:         warehouse.Lng,
			Status:      uint8(inventory.DeliverStatus_ExpressOrderDelivering),
			CreateTime:  now,
			UpdateTime:  now,
		}
		if err = s.dao.Create(ctx, &expressOrder); err != nil {
			return
		}
		if err = s.dao.TransContext(ctx).Model(&model.ProcessOrder{}).Where(
			"id = ?", processOrder.ID).Updates(
			map[string]interface{}{
				"update_time":         now,
				"express_order_id":    expressOrder.ID,
				"express_platform_id": platform.ID,
			}).Error; err != nil {
			return
		}
		var prepareTime, deliverTime string
		if processOrder.PrepareTime.Valid {
			prepareTime = processOrder.PrepareTime.Time.Format(util.DefaultLayout)
		}
		if processOrder.DeliverTime.Valid {
			deliverTime = processOrder.DeliverTime.Time.Format(util.DefaultLayout)
		}
		statsMsg, _ := json.Marshal(&inventory.InventoryOrderUpdatePayload{
			OrderNumber:    processOrder.OrderNumber,
			ExtOrderNumber: processOrder.ExtOrderNumber,
			Source:         inventory.SourceEnum(processOrder.Source),
			Timestamp:      now.Unix(),
			Status:         inventory.ProcessOrderStatus(processOrder.Status),
			DeliverType:    inventory.DeliverType(processOrder.DeliverType),
			DeliverMemo: &inventory.InventoryOrderUpdatePayload_DeliverMemo{
				Platform:         platform.Name,
				PlatformOrderNum: req.PlatformOrderNumber,
				DeliverTime:      deliverTime,
				PrepareTime:      prepareTime,
				ExtractKey:       processOrder.ExtractKey.String,
			},
		})
		if err = s.dao.PublishUniqueNSQMessage(ctx, constant.TopicInventoryStatus, statsMsg); err != nil {
			return err
		}
		reply = &inventory.CreateOrderReply{
			OrderNumber:    orderNumber,
			ExtOrderNumber: req.ExtOrderNumber,
			Status:         uint32(processOrder.Status),
		}
		return
	})
	if err != nil {
		return
	}
	return
}

// GetRefundProcessOrders 获取进行中退货单列表
func (s *Service) GetRefundProcessOrders(ctx context.Context, req *inventory.GetRefundOrdersRequest) (resp *inventory.GetRefundProcessOrdersResponse, err error) {
	var endDate, startDate time.Time
	empId := req.WebToken.EmpId
	if startDate, endDate, err = verifyStartDateAndEndDate(req.StartDate, req.EndDate); err != nil {
		return
	}
	if _, err = s.CheckUsagePermission(ctx, empId, req.PStoreId, req.Id, constant.RefundManagement); err != nil {
		return
	}
	var (
		status        []int
		platformIDs   []int
		total         int64
		processOrders []*model.ProcessOrder
		expressOrders []*model.ExpressOrder
		platforms     []*model.ExpressPlatform
	)
	if req.QueryStatus != 0 {
		status = []int{int(req.QueryStatus)}
	} else {
		status = []int{int(inventory.ProcessOrderStatus_ProcessOrderReturning)}
	}
	zeroTime := time.Time{}
	if total, processOrders, err = s.dao.FindProcessOrders(ctx, int(req.Id), status, req.QuerySource,
		[]string{}, "", startDate, endDate, zeroTime, zeroTime,
		zeroTime, zeroTime, int(req.PageNo), int(req.PageSize), req.QueryRefundOrderNumber, req.QueryExtOrderNumber,
		constant.DirectionOut); err != nil {
		return
	}
	orderNumbers := make([]string, len(processOrders))
	for i, o := range processOrders {
		orderNumbers[i] = o.OrderNumber
		if o.ExpressPlatformID.Valid {
			platformIDs = append(platformIDs, int(o.ExpressPlatformID.Int64))
		}
	}
	if expressOrders, err = s.dao.FindExpressOrdersByOrderNumbers(ctx, orderNumbers); err != nil {
		return
	}
	expressRef := make(map[string]*model.ExpressOrder, len(expressOrders))
	for _, o := range expressOrders {
		expressRef[o.OrderNumber] = o
	}
	platformRef := make(map[uint32]string, len(platformIDs))
	if len(platformIDs) > 0 {
		if platforms, err = s.dao.FindPlatformByIDs(ctx, platformIDs); err != nil {
			return
		}
		for _, p := range platforms {
			platformRef[p.ID] = p.Name
		}
	}
	views := make([]*inventory.RefundProcessOrderView, len(processOrders))
	for i, o := range processOrders {
		views[i] = &inventory.RefundProcessOrderView{
			RefundOrderNumber:   o.OrderNumber,
			OrderNumber:         o.ExtOrderNumber,
			Source:              inventory.SourceEnum(o.Source),
			SubmitTime:          o.SubmitTime.Format(util.DefaultLayout),
			PlatformName:        platformRef[uint32(o.ExpressPlatformID.Int64)],
			PlatformOrderNumber: expressRef[o.OrderNumber].TradeNo,
			RefundProdCount:     o.ProdCount,
			RefundAmount:        o.ProdTrans,
			Status:              inventory.ProcessOrderStatus(o.Status),
			// 待处理退货单不存在拒绝原因
		}
		if o.DeliverTime.Valid {
			views[i].DeliverTime = o.DeliverTime.Time.Format(util.DefaultLayout)
		}
		if o.CompleteTime.Valid {
			views[i].CompleteTime = o.CompleteTime.Time.Format(util.DefaultLayout)
		}
	}
	resp = &inventory.GetRefundProcessOrdersResponse{
		Total:  uint32(total),
		Orders: views,
	}
	return
}

// GetRefundFinishedOrders 获取已完成退货单列表
func (s *Service) GetRefundFinishedOrders(ctx context.Context, req *inventory.GetRefundOrdersRequest) (resp *inventory.GetRefundProcessOrdersResponse, err error) {
	var endDate, startDate time.Time
	empId := req.WebToken.EmpId
	if startDate, endDate, err = verifyStartDateAndEndDate(req.StartDate, req.EndDate); err != nil {
		return
	}
	if _, err = s.CheckUsagePermission(ctx, empId, req.PStoreId, req.Id, constant.RefundManagement); err != nil {
		return
	}
	var (
		status        []int
		platformIDs   []int
		total         int64
		processOrders []*model.ProcessOrder
		expressOrders []*model.ExpressOrder
		platforms     []*model.ExpressPlatform
	)
	if req.QueryStatus != 0 {
		status = []int{int(req.QueryStatus)}
	} else {
		status = []int{
			int(inventory.ProcessOrderStatus_ProcessOrderRejectReturn),
			int(inventory.ProcessOrderStatus_ProcessOrderReturned)}
	}
	zeroTime := time.Time{}
	if total, processOrders, err = s.dao.FindProcessOrders(ctx, int(req.Id), status, req.QuerySource,
		[]string{}, "", startDate, endDate, zeroTime, zeroTime,
		zeroTime, zeroTime, int(req.PageNo), int(req.PageSize), req.QueryRefundOrderNumber, req.QueryExtOrderNumber,
		constant.DirectionOut); err != nil {
		return
	}
	orderNumbers := make([]string, len(processOrders))
	for i, o := range processOrders {
		orderNumbers[i] = o.OrderNumber
		if o.ExpressPlatformID.Valid {
			platformIDs = append(platformIDs, int(o.ExpressPlatformID.Int64))
		}
	}
	if expressOrders, err = s.dao.FindExpressOrdersByOrderNumbers(ctx, orderNumbers); err != nil {
		return
	}
	expressRef := make(map[string]*model.ExpressOrder, len(expressOrders))
	for _, o := range expressOrders {
		expressRef[o.OrderNumber] = o
	}
	platformRef := make(map[uint32]string, len(platformIDs))
	if len(platformIDs) > 0 {
		if platforms, err = s.dao.FindPlatformByIDs(ctx, platformIDs); err != nil {
			return
		}
		for _, p := range platforms {
			platformRef[p.ID] = p.Name
		}
	}
	views := make([]*inventory.RefundProcessOrderView, len(processOrders))
	for i, o := range processOrders {
		remark := &inventory.Remark{}
		if err = json.Unmarshal(o.Remark, remark); err != nil {
			return
		}
		views[i] = &inventory.RefundProcessOrderView{
			RefundOrderNumber:   o.OrderNumber,
			OrderNumber:         o.ExtOrderNumber,
			Source:              inventory.SourceEnum(o.Source),
			SubmitTime:          o.SubmitTime.Format(util.DefaultLayout),
			PlatformName:        platformRef[uint32(o.ExpressPlatformID.Int64)],
			PlatformOrderNumber: expressRef[o.OrderNumber].TradeNo,
			RefundProdCount:     o.ProdCount,
			RefundAmount:        o.ProdTrans,
			Status:              inventory.ProcessOrderStatus(o.Status),
			Reason:              remark.Reason,
		}
		if o.DeliverTime.Valid {
			views[i].DeliverTime = o.DeliverTime.Time.Format(util.DefaultLayout)
		}
		if o.CompleteTime.Valid {
			views[i].CompleteTime = o.CompleteTime.Time.Format(util.DefaultLayout)
		}
	}
	resp = &inventory.GetRefundProcessOrdersResponse{
		Total:  uint32(total),
		Orders: views,
	}
	return
}

// GetRefundProcessOrderInfo 获取履约退款单详情
func (s *Service) GetRefundProcessOrderInfo(ctx context.Context, req *inventory.CommonOrderRequest) (resp *inventory.GetRefundProcessOrderInfoResponse, err error) {
	var (
		warehouse      *model.Warehouse
		processOrder   *model.ProcessOrder
		processDetails []*model.ProcessDetail
		expressOrder   *model.ExpressOrder
		platform       *model.ExpressPlatform
		members        []*bmember.MemberMobileNickname
		mainRef        []*model.ProcessDetail
		contact        string
	)
	empId := req.WebToken.EmpId
	if warehouse, err = s.CheckUsagePermission(ctx, empId, req.PStoreId, req.Id, constant.RefundManagement); err != nil {
		return
	}
	if processOrder, err = s.dao.GetProcessOrder(ctx, req.OrderNumber); err != nil {
		if errors.Cause(err) == gorm.ErrRecordNotFound {
			err = constant.ErrOrderNotExist
		}
		return
	}
	if processDetails, err = s.dao.FindProcessOrderDetails(ctx, processOrder.ID); err != nil {
		return
	}
	if processOrder.ExpressOrderID.Valid {
		if expressOrder, err = s.dao.GetExpressOrderByID(ctx, uint64(processOrder.ExpressOrderID.Int64)); err != nil {
			return
		}
	} else {
		err = constant.ErrSystemError
		return
	}
	if platform, err = s.dao.GetPlatform(ctx, expressOrder.PlatformID); err != nil {
		return
	}
	if members, err = s.downstream.CallMemberQuery(ctx, []string{processOrder.MemberID}); err != nil {
		return
	}
	if len(members) == 1 && strconv.Itoa(int(members[0].MemberId)) == processOrder.MemberID {
		contact = fmt.Sprintf("%s/%s", members[0].Nickname, members[0].Mobile)
	}
	subRef := make(map[uint64][]*model.ProcessDetail)
	for _, d := range processDetails {
		if d.ParentID == 0 {
			mainRef = append(mainRef, d)
			subRef[d.ID] = []*model.ProcessDetail{}
		} else {
			subRef[d.ParentID] = append(subRef[d.ParentID], d)
		}
	}
	views := make([]*inventory.ProcessOrderDetailView, len(processDetails))
	for i, d := range processDetails {
		var subViews []*inventory.ProcessOrderDetailView
		if subs, hasSub := subRef[d.ID]; hasSub || len(subs) == 0 {
			for _, sub := range subs {
				r := model.SnapShotRemark{}
				if err = json.Unmarshal(d.Remark, &r); err != nil {
					return
				}
				subViews = append(subViews, &inventory.ProcessOrderDetailView{
					StorageKey: sub.StorageKey,
					Price:      sub.Price,
					Quantity:   sub.Quantity,
					Name:       r.Name,
					Code:       r.Code,
					TypeName:   r.Type,
					Unit:       r.Unit,
					Amount:     sub.Trans,
					Status:     inventory.ProcessOrderStatus(processOrder.Status), // 这里返回订单状态
				})
			}
		}
		r := model.SnapShotRemark{}
		if err = json.Unmarshal(d.Remark, &r); err != nil {
			return
		}
		views[i] = &inventory.ProcessOrderDetailView{
			StorageKey: d.StorageKey,
			Price:      d.Price,
			Quantity:   d.Quantity,
			Name:       r.Name,
			SkuName:    r.SkuName,
			Code:       r.Code,
			TypeName:   r.Type,
			Unit:       r.Unit,
			Amount:     d.Trans,
			Status:     inventory.ProcessOrderStatus(processOrder.Status),
			Subs:       subViews, // 这里返回订单状态
		}

	}
	r := inventory.Remark{}
	_ = json.Unmarshal(processOrder.Remark, &r)
	resp = &inventory.GetRefundProcessOrderInfoResponse{
		OrderNumber:         processOrder.OrderNumber,
		ExtOrderNumber:      processOrder.ExtOrderNumber,
		Status:              inventory.ProcessOrderStatus(processOrder.Status),
		DeliverStatus:       fmt.Sprintf("%s/%s", platform.Name, expressOrder.TradeNo),
		SubmitTime:          processOrder.SubmitTime.Format(util.DefaultLayout),
		WarehouseName:       warehouse.Name,
		Source:              inventory.SourceEnum(processOrder.Source),
		Contact:             contact,
		Memo:                r.Memo,
		Reason:              r.Reason,
		DeliverType:         inventory.DeliverType(processOrder.DeliverType),
		PlatformName:        platform.Name,
		PlatformOrderNumber: expressOrder.TradeNo,
		ProdTrans:           processOrder.ProdTrans,
		ProdCount:           processOrder.ProdCount,
		Details:             views,
	}
	if processOrder.CompleteTime.Valid {
		resp.CompleteTime = processOrder.CompleteTime.Time.Format(util.DefaultLayout)
	}
	return
}

// AgreeRefund 同意退款
func (s *Service) AgreeRefund(ctx context.Context, req *inventory.CommonOrderRequest) (resp *inventory.DefaultResp, err error) {
	empId := req.WebToken.EmpId
	resp = &inventory.DefaultResp{
		Status: true,
	}
	err = s.dao.Transaction(ctx, func(ctx context.Context) (err error) {
		var (
			details      []*model.ProcessDetail
			expressOrder *model.ExpressOrder
			platform     *model.ExpressPlatform
		)
		if _, err = s.CheckUsagePermission(ctx, empId, req.PStoreId, req.Id, constant.RefundManagement); err != nil {
			return
		}
		processOrder := model.ProcessOrder{OrderNumber: req.OrderNumber}
		if err = s.dao.Lock(ctx, processOrder, &processOrder); err != nil {
			if errors.Cause(err) == gorm.ErrRecordNotFound {
				err = constant.ErrOrderNotExist
			}
			return
		}
		if inventory.ProcessOrderStatus(processOrder.Status) != inventory.ProcessOrderStatus_ProcessOrderReturning {
			err = constant.ErrProcessStatus
			return
		}
		if processOrder.ExpressOrderID.Valid {
			if expressOrder, err = s.dao.GetExpressOrderByID(ctx, uint64(processOrder.ExpressOrderID.Int64)); err != nil {
				return
			}
		} else {
			err = constant.ErrSystemError
			return
		}
		if platform, err = s.dao.GetPlatform(ctx, expressOrder.PlatformID); err != nil {
			return
		}
		if details, err = s.dao.FindProcessOrderDetails(ctx, processOrder.ID); err != nil {
			return
		}
		now := time.Now()
		expressOrder.Status = uint8(inventory.DeliverStatus_ExpressOrderReceived)
		expressOrder.UpdateTime = now
		processOrder.RefundFlag = 0
		processOrder.Status = uint8(inventory.ProcessOrderStatus_ProcessOrderReturned)
		processOrder.Operator = empId
		processOrder.CompleteTime = util.NullTime{Valid: true, Time: now}
		processOrder.UpdateTime = now
		if err = s.dao.Save(ctx, processOrder); err != nil {
			return
		}
		if err = s.dao.Save(ctx, expressOrder); err != nil {
			return
		}
		if err = s.dao.UpdateProcessDetailStatus(ctx, processOrder.ID, constant.ProcessDetailShipped, now); err != nil {
			return
		}
		storageKeyMap := make(util.StringBoolMap)
		materialIDMap := make(util.UInt64BoolMap)
		for _, detail := range details {
			if detail.IsAccounting {
				var affected bool
				if affected, err = s.dao.DecreaseStorage(
					ctx, req.Id, detail.StorageKey, -detail.Quantity, now); err != nil {
					return
				}
				if affected {
					storageKeyMap[detail.StorageKey] = true
					if inventory.StorageType(detail.StorageType) == inventory.StorageType_MATERIAL {
						materialID, _ := strconv.Atoi(detail.StorageKey[2:])
						materialIDMap[uint64(materialID)] = true
					}
				}
			}
		}
		// 状态同步
		var deliverTime string
		if processOrder.DeliverTime.Valid {
			deliverTime = processOrder.DeliverTime.Time.Format(util.DefaultLayout)
		}
		statsMsg, _ := json.Marshal(&inventory.InventoryOrderUpdatePayload{
			OrderNumber:    processOrder.OrderNumber,
			ExtOrderNumber: processOrder.ExtOrderNumber,
			Source:         inventory.SourceEnum(processOrder.Source),
			Timestamp:      now.Unix(),
			Status:         inventory.ProcessOrderStatus(processOrder.Status),
			DeliverType:    inventory.DeliverType(processOrder.DeliverType),
			RefundMemo: &inventory.InventoryOrderUpdatePayload_RefundMemo{
				Platform:         platform.Name,
				PlatformOrderNum: expressOrder.TradeNo,
				DeliverTime:      deliverTime,
				CompleteTime:     now.Format(util.DefaultLayout),
				Operator:         req.WebToken.EmpName,
			},
		})
		if err = s.dao.PublishUniqueNSQMessage(ctx, constant.TopicInventoryStatus, statsMsg); err != nil {
			return err
		}
		return
	})
	return
}

// RejectRefund 拒绝退款
func (s *Service) RejectRefund(ctx context.Context, req *inventory.RejectRefundProcessRequest) (resp *inventory.DefaultResp, err error) {
	empId := req.WebToken.EmpId
	resp = &inventory.DefaultResp{
		Status: true,
	}
	err = s.dao.Transaction(ctx, func(ctx context.Context) (err error) {
		var (
			expressOrder *model.ExpressOrder
			remarkJSON   json.RawMessage
			platform     *model.ExpressPlatform
		)
		if _, err = s.CheckUsagePermission(ctx, empId, req.PStoreId, req.Id, constant.RefundManagement); err != nil {
			return
		}
		processOrder := model.ProcessOrder{OrderNumber: req.OrderNumber}
		if err = s.dao.Lock(ctx, processOrder, &processOrder); err != nil {
			if errors.Cause(err) == gorm.ErrRecordNotFound {
				err = constant.ErrOrderNotExist
			}
			return
		}
		if inventory.ProcessOrderStatus(processOrder.Status) != inventory.ProcessOrderStatus_ProcessOrderReturning {
			err = constant.ErrProcessStatus
			return
		}
		if processOrder.ExpressOrderID.Valid {
			if expressOrder, err = s.dao.GetExpressOrderByID(ctx, uint64(processOrder.ExpressOrderID.Int64)); err != nil {
				return
			}
		} else {
			err = constant.ErrSystemError
			return
		}
		if platform, err = s.dao.GetPlatform(ctx, expressOrder.PlatformID); err != nil {
			return
		}
		now := time.Now()
		processRemark := inventory.Remark{}
		if err = json.Unmarshal(processOrder.Remark, &processRemark); err != nil {
			return
		}
		processRemark.Reason = req.Reason
		if remarkJSON, err = json.Marshal(processRemark); err != nil {
			return
		}
		processOrder.Status = uint8(inventory.ProcessOrderStatus_ProcessOrderRejectReturn)
		processOrder.RefundFlag = 0
		processOrder.Remark = orm.JSON(remarkJSON)
		processOrder.Operator = empId
		processOrder.UpdateTime = now
		expressOrder.Status = uint8(inventory.DeliverStatus_ExpressOrderFailed)
		expressOrder.UpdateTime = now
		if err = s.dao.Save(ctx, processOrder); err != nil {
			return
		}
		if err = s.dao.Save(ctx, expressOrder); err != nil {
			return
		}
		if err = s.dao.UpdateProcessDetailStatus(ctx, processOrder.ID, constant.ProcessDetailCancelled, now); err != nil {
			return
		}
		// 状态同步
		var deliverTime string
		if processOrder.DeliverTime.Valid {
			deliverTime = processOrder.DeliverTime.Time.Format(util.DefaultLayout)
		}
		statsMsg, _ := json.Marshal(&inventory.InventoryOrderUpdatePayload{
			OrderNumber:    processOrder.OrderNumber,
			ExtOrderNumber: processOrder.ExtOrderNumber,
			Source:         inventory.SourceEnum(processOrder.Source),
			Timestamp:      now.Unix(),
			Status:         inventory.ProcessOrderStatus(processOrder.Status),
			DeliverType:    inventory.DeliverType(processOrder.DeliverType),
			RefundMemo: &inventory.InventoryOrderUpdatePayload_RefundMemo{
				Platform:         platform.Name,
				PlatformOrderNum: expressOrder.TradeNo,
				DeliverTime:      deliverTime,
				CompleteTime:     now.Format(util.DefaultLayout),
				Reason:           req.Reason,
				Operator:         req.WebToken.EmpName,
			},
		})
		if err = s.dao.PublishUniqueNSQMessage(ctx, constant.TopicInventoryStatus, statsMsg); err != nil {
			return err
		}
		return
	})
	return
}
